﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using FinPlusInterfaces;

namespace FinPlusBaseCore
{
    public class Throttle : IThrottle, IDisposable
    {
        public bool IsBusy { get; set;}

        private Action<IDictionary<string, object>> _sendAction;
        private ConcurrentDictionary<string, DateTime> _timeStamps;
        private ConcurrentDictionary<string, object> _queue, _cache;
        private static int _key;
        private int _throttle;
        private bool _hasCache;
        private Timer _timer;

        //construct
        public Throttle(int throttle) : this(throttle, false, false) { }

        public Throttle(int throttle, bool isBusy) : this(throttle, isBusy, false) { }

        public Throttle(int throttle, bool isBusy, bool hasCache)
        {
            _throttle = throttle;
            IsBusy = isBusy;
            _hasCache = hasCache;
            _timeStamps = new ConcurrentDictionary<string, DateTime>();
            _queue = new ConcurrentDictionary<string, object>();
            _cache = new ConcurrentDictionary<string, object>();
        }

        //public
        public void Start(Action<IDictionary<string, object>> sendAction)
        {
            if (_sendAction != null) 
                return;
            _sendAction = sendAction;
            _timer = new Timer(_throttle);
            _timer.Elapsed += Callback;
        }

        public void Put(object value) {Put(_key++.ToString(), value);}

        public void Put(string key, object value){Put(key, value, DateTime.Now);}

        public void Put(string key, object value, DateTime timeStamp)
        {
            lock (_queue)
            {
                if (_timeStamps.ContainsKey(key) && _timeStamps[key] > timeStamp)
                    return;

                _queue.TryAdd(key,  value);
                _timeStamps.TryAdd(key, timeStamp);

                if (_hasCache)
                    _cache.TryAdd(key, value);

                if (_timer != null && !_timer.Enabled)
                    _timer.Start();
            }
        }

        public void Resend(string key)
        {
            lock (_queue)
            {
                foreach(var kvp in _cache.Where(i=>i.Key.StartsWith(key)))
                    _queue.TryAdd(kvp.Key, _cache[kvp.Key]);

                if (_timer != null && !_timer.Enabled)
                    _timer.Start();
            }
        }

        public void Dispose()
        {
            if (_timer != null)
            {
                _timer.Stop();
                _timer.Dispose();
            }
        }

        //private
        private IDictionary<string, object> Take()
        {
            lock (_queue)
            {
                if (_timer != null) 
                    _timer.Stop();

                IsBusy = true;

                var take = _queue;
                _queue = new ConcurrentDictionary<string, object>();

                return new Dictionary<string, object>(take);
            }
        }

        private void Callback(object s, ElapsedEventArgs a)
        {
            if (IsBusy || _queue.Count < 1) 
                return;

            _sendAction.Invoke(Take());
        }
    }
}
